﻿using System;
using System.Diagnostics.Contracts;
using Isuka.Iteration;

namespace Isuka.Excel.Iteration
{
    /// <summary>
    /// XRange に含まれる範囲を反復する
    /// </summary>
    public sealed class XRangeLinerIterator
        : IRandomAccessIterator<XRange>
    {
        readonly XRange container;
        readonly XDirection direction;
        readonly int shift;
        int index;

        public XRangeLinerIterator(XRange container, XDirection direction, int index = 0)
        {
            Contract.Requires<ArgumentNullException>(container != null, "container");

            this.container = container;
            this.direction = direction;
            this.shift = direction == XDirection.Down || direction == XDirection.ToRight ? 1 : -1;
            this.index = index;
        }

        bool EqualsCore(XRangeLinerIterator other)
        {
            Contract.Requires(other != null);
            // TODO: 比較条件を書いてください
            return container.Equals(other.container) && direction == other.direction && index == other.index;
        }

        #region IIterator<T> メンバー

        public XRange Current
        {
            get
            {
                Gate.Positive(index);
                if (direction == XDirection.Down || direction == XDirection.Up)
                {
                    return container[index];
                }
                else
                {
                    return container[container.Row, index];
                }
            }
        }

        #endregion

        #region IIterator メンバー

        public bool Advance(int diff)
        {
            checked
            {
                index += (diff * shift);
                return InRange;
            }
        }

        public int? Bias
        {
            get { return index; }
        }

        public object Container
        {
            get { return container; }
        }

        object IIterator.Current
        {
            get { return Current; }
        }

        public bool Equals(IIterator other)
        {
            if (other == null)
            {
                return !InRange;
            }
            else
            {
                if (other is XRangeLinerIterator)
                {
                    return EqualsCore((XRangeLinerIterator)other);
                }
            }
            return false;
        }

        public int? Distance(IIterator other)
        {
            var it = other as XRangeLinerIterator;
            if (it != null && container == it.container && direction == it.direction)
            {
                return it.index - index;
            }
            return null;
        }

        public bool InRange
        {
            get
            {
                var count = direction.IsVirtical() ? container.Rows.Count : container.Columns.Count;
                return 0 <= index && index < count;
            }
        }

        public void MoveNext()
        {
            checked
            {
                index += shift;
            }
        }

        #endregion

        #region ICloneable メンバー

        public object Clone()
        {
            return new XRangeLinerIterator(container, direction, index);
        }

        #endregion

        #region IRandomAccessIterator メンバー

        public int Index
        {
            get { return index; }
        }

        #endregion

        #region IBidirectionalIterator メンバー

        public void MovePrev()
        {
            checked
            {
                index -= shift;
            }
        }

        #endregion

        #region Object メンバー

        // override object.Equals
        public override bool Equals(object obj)
        {
            if (obj == null)
            {
                return !InRange;
            }
            else
            {
                if (obj is XRangeLinerIterator)
                {
                    return EqualsCore((XRangeLinerIterator)obj);
                }
            }
            return false;
        }

        // override object.GetHashCode
        public override int GetHashCode()
        {
            // TODO: write your implementation of GetHashCode() here
            return container.GetHashCode();
        }

        #endregion

        #region ContractInvariantMethod

        [ContractInvariantMethod]
        private void ObjectInvariant()
        {
            Contract.Invariant(container != null);
        }

        #endregion
    }
}
